package com.wang.lottery.domain.activity.service.partake;

import com.wang.lottery.common.Constants;
import com.wang.lottery.common.Result;
import com.wang.lottery.domain.activity.model.po.UserTakeActivity;
import com.wang.lottery.domain.activity.model.req.PartakeReq;
import com.wang.lottery.domain.activity.model.res.PartakeResult;
import com.wang.lottery.domain.activity.model.res.StockResult;
import com.wang.lottery.domain.activity.model.vo.ActivityBillVO;
import com.wang.lottery.domain.support.ids.IIdGenerator;

import javax.annotation.Resource;
import java.util.Map;

/**
 * @author wjh
 * @create 2023-11-08 13:25
 * 活动领取模板抽象类
 */
public abstract class BaseActivityPartake extends ActivityPartakeSupport implements IActivityPartake{
    
    @Resource
    private Map<Constants.Ids, IIdGenerator> idGeneratorMap;
    /**
     * 参与活动
     *
     * @param req 入参
     * @return 领取结果
     */
    @Override
    public PartakeResult doPartake(PartakeReq req) {
        //判断幂等性
        UserTakeActivity userTakeActivity = queryNoConsumedTakeActivityOrder(req.getActivityId(), req.getuId());
        if (userTakeActivity != null) {
            //如果存在已领取但未抽奖的活动账单就执行抽奖就好了
            return buildPartakeResult(userTakeActivity.getStrategyId(),userTakeActivity.getTakeId());
        }
        // 查询活动账单，查活动表和用户活动参与次数表
        ActivityBillVO activityBillVO = super.activityRepository.queryActivityBill(req);
        //校验活动账单
        Result result = this.checkActivityBill(req, activityBillVO);
        if (!Constants.ResponseCode.SUCCESS.getCode().equals(result.getCode())) {
            return new PartakeResult(result.getCode(),result.getInfo());
        }
        //扣减活动库存，通过Redis【活动库存扣减编号，作为锁的Key，缩小颗粒度】 Begin
        StockResult stockResult = this.subtractionActivityStockByRedis(req,activityBillVO.getStockSurplusCount());
        if (!Constants.ResponseCode.SUCCESS.getCode().equals(stockResult.getCode())) {
            return new PartakeResult(result.getCode(),result.getInfo());
        }
        // 领取活动信息【个人用户把活动信息写入到用户表】
        // 这里获取雪花算法生成活动id
        long takeId = idGeneratorMap.get(Constants.Ids.SnowFlake).nextId();
        result = this.grabActivity(req,activityBillVO,takeId);
        if (!Constants.ResponseCode.SUCCESS.getCode().equals(result.getCode())) {
            return new PartakeResult(result.getCode(),result.getInfo());
        }
        this.recoverActivityCacheStockByRedis(stockResult.getStockKey());
        // 封装结果【返回的策略ID，用于继续完成抽奖步骤】
        PartakeResult partakeResult = buildPartakeResult(activityBillVO.getStrategyId(), takeId);
        partakeResult.setStockSurplusCount(stockResult.getStockSurplusCount());
        return partakeResult;
    }

    /**
     * 释放活动库存锁
     * @param stockKey 库存锁
     */
    protected abstract void recoverActivityCacheStockByRedis(String stockKey);

    /**
     * 为领取活动方法封装返回结果
     * @param strategyId 策略id
     * @param takeId 领取id
     * @return 返回结果
     */
    private PartakeResult buildPartakeResult(Long strategyId, Long takeId) {
        PartakeResult partakeResult = new PartakeResult(Constants.ResponseCode.SUCCESS.getCode(), Constants.ResponseCode.SUCCESS.getInfo());
        partakeResult.setStrategyId(strategyId);
        partakeResult.setTakeId(takeId);
        return partakeResult;
    }

    /**
     * 活动信息校验处理，把活动库存、状态、日期、个人参与次数
     *
     * @param partake 参与活动请求
     * @param bill    活动账单
     * @return 校验结果
     */
    protected abstract Result checkActivityBill(PartakeReq partake, ActivityBillVO bill);
    
    /**
     * 扣减活动库存
     *
     * @param req 参与活动请求
     * @param stockCount 剩余库存数量
     * @return 扣减结果
     */
    protected abstract StockResult subtractionActivityStockByRedis(PartakeReq req, Integer stockCount);
    
    /**
     * 领取活动
     *
     * @param partake 参与活动请求
     * @param bill    活动账单
     * @return 领取结果
     */
    protected abstract Result grabActivity(PartakeReq partake, ActivityBillVO bill, Long takeId);

    /**
     * 查询是否存在未执行抽奖领取活动单【user_take_activity 存在 state = 0，领取了但抽奖过程失败的，可以直接返回领取结果继续抽奖】
     *
     * @param activityId 活动ID
     * @param uId        用户ID
     * @return 领取单
     */
    protected abstract UserTakeActivity queryNoConsumedTakeActivityOrder(Long activityId, String uId);
}
